package com.cruise.game.service.impl.common;

import com.cruise.game.common.api.ApiException;
import com.cruise.game.common.constants.SecurityConstants;
import com.cruise.game.common.enums.RoleEnum;
import com.cruise.game.common.enums.UserStatusEnum;
import com.cruise.game.common.utils.JwtUtils;
import com.cruise.game.mapper.UserMapper;
import com.cruise.game.model.entity.User;
import com.cruise.game.model.param.common.SetAdminParam;
import com.cruise.game.model.param.common.WxLoginParam;
import com.cruise.game.model.vo.common.LoginResultVO;
import com.cruise.game.model.vo.common.TokenVO;
import com.cruise.game.service.common.AuthService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;

/**
 * 认证服务实现
 *
 * @author cruise
 * @date 2025-02-28
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private JwtUtils jwtUtils;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Value("${wx.miniapp.appid}")
    private String appid;

    @Value("${wx.miniapp.secret}")
    private String secret;

    @Override
    public LoginResultVO wxLogin(WxLoginParam param) {
        log.info("微信登录开始处理，code: {}", param.getCode());

        // 调用微信API获取openid
        String openid = getWxOpenid(param.getCode());
        log.info("获取到openid: {}", openid);

        // 根据openid查询用户
        User user = userMapper.selectByOpenid(openid);

        if (user == null) {
            // 用户不存在，进行注册流程
            log.info("用户不存在，开始注册流程，openid: {}", openid);
            user = registerNewUser(openid);
            log.info("新用户注册成功，userId: {}", user.getId());
        } else {
            // 用户存在，直接登录
            log.info("用户已存在，直接登录，userId: {}", user.getId());
        }

        // 用户被禁用
        if (UserStatusEnum.isBanned(user.getStatus())) {
            log.error("用户账号已被禁用，userId: {}", user.getId());
            throw new ApiException("账号已被禁用");
        }

        // 生成token
        String token = generateToken(user);

        // 构建登录结果
        return buildLoginResult(user, token);
    }

    /**
     * 通过微信API获取openid
     *
     * @param code 微信登录临时凭证
     * @return openid
     */
    private String getWxOpenid(String code) {
        // 创建RestTemplate
        RestTemplate restTemplate = new RestTemplate();

        // 微信小程序登录URL
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";

        // 请求参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", appid);
        params.put("secret", secret);
        params.put("code", code);

        try {
            // 发送请求
            ResponseEntity<String> response = restTemplate.getForEntity(url, String.class, params);
            String responseBody = response.getBody();

            if (responseBody == null || responseBody.isEmpty()) {
                log.error("微信登录请求返回为空");
                throw new ApiException("微信授权失败，请重试");
            }

            // 解析JSON响应
            JsonNode jsonNode = objectMapper.readTree(responseBody);

            // 检查微信返回是否有错误
            if (jsonNode.has("errcode") && jsonNode.get("errcode").asInt() != 0) {
                log.error("微信登录失败: errcode={}, errmsg={}",
                        jsonNode.get("errcode").asText(),
                        jsonNode.get("errmsg").asText());
                throw new ApiException("微信授权失败: " + jsonNode.get("errmsg").asText());
            }

            // 获取openid
            if (!jsonNode.has("openid")) {
                log.error("微信登录返回数据不包含openid");
                throw new ApiException("微信授权失败，请重试");
            }

            return jsonNode.get("openid").asText();

        } catch (JsonProcessingException e) {
            log.error("解析微信登录响应JSON失败", e);
            throw new ApiException("微信授权失败，请重试");
        } catch (Exception e) {
            log.error("调用微信登录接口失败", e);
            throw new ApiException("微信授权失败，请重试");
        }
    }

    /**
     * 注册新用户
     *
     * @param openid 微信openid
     * @return 新注册的用户
     */
    private User registerNewUser(String openid) {
        User user = new User();
        user.setOpenid(openid);
        user.setNickname("用户_" + openid.substring(openid.length() - 6));
        user.setAvatarUrl("https://example.com/default_avatar.jpg");
        user.setBalance(BigDecimal.ZERO);
        user.setRole(RoleEnum.USER.getValue());
        user.setStatus(UserStatusEnum.ACTIVE.getValue());

        LocalDateTime now = LocalDateTime.now();
        user.setCreatedAt(now);
        user.setUpdatedAt(now);

        userMapper.insert(user);
        return user;
    }

    @Override
    public TokenVO refreshToken(String token) {
        if (token == null) {
            throw new ApiException("令牌不能为空");
        }

        try {
            // 即使令牌已过期，也尝试获取用户ID
            Long userId = jwtUtils.getUserIdFromToken(token);

            // 从数据库中获取最新的用户信息
            User user = userMapper.selectById(userId);
            if (user == null) {
                throw new ApiException("用户不存在");
            }

            // 使用最新的用户信息生成新token
            String refreshedToken = generateToken(user);

            return new TokenVO(refreshedToken);
        } catch (ExpiredJwtException e) {
            // 令牌已过期，但我们仍然可以从中提取Claims
            Claims claims = e.getClaims();
            if (claims == null || !claims.containsKey("userId")) {
                throw new ApiException("无效的令牌结构");
            }

            // 从过期令牌中提取用户ID
            Long userId = Long.valueOf(claims.get("userId").toString());

            // 从数据库获取用户信息
            User user = userMapper.selectById(userId);
            if (user == null) {
                throw new ApiException("用户不存在");
            }

            // 生成新令牌
            String refreshedToken = generateToken(user);

            return new TokenVO(refreshedToken);
        } catch (Exception e) {
            // 处理其他可能的异常
            log.error("刷新令牌时发生错误", e);
            throw new ApiException("无效的令牌: " + e.getMessage());
        }
    }

    @Override
    public LoginResultVO setAdmin(SetAdminParam param, Long userId) {
        // 验证密钥
        if (!Objects.equals(param.getSecretKey(), SecurityConstants.ADMIN_SECRET_KEY)) {
            throw new ApiException("密钥错误");
        }

        // 获取用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ApiException("用户不存在");
        }

        // 设置为管理员
        user.setRole(RoleEnum.ADMIN.getValue());
        user.setUpdatedAt(LocalDateTime.now());
        userMapper.updateById(user);

        // 生成新token
        String token = generateToken(user);

        // 构建登录结果
        return buildLoginResult(user, token);
    }

    @Override
    public String generateToken(User user) {
        // 确保角色格式正确（去除可能的ROLE_前缀，因为在JwtAuthenticationFilter中会重新添加）
        String role = user.getRole();
        if (role.toLowerCase().startsWith(SecurityConstants.ROLE_PREFIX.toLowerCase())) {
            role = role.substring(SecurityConstants.ROLE_PREFIX.length());
        }

        return jwtUtils.generateToken(user.getId(), role.toLowerCase());
    }

    @Override
    public LoginResultVO buildLoginResult(User user, String token) {
        LoginResultVO.UserInfoVO userInfoVO = new LoginResultVO.UserInfoVO();
        userInfoVO.setId(user.getId());
        userInfoVO.setNickname(user.getNickname());
        userInfoVO.setAvatar(user.getAvatarUrl());
        userInfoVO.setRole(user.getRole());
        userInfoVO.setBalance(user.getBalance());

        return new LoginResultVO(token, userInfoVO);
    }
}